package com.nulldev.util.io;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Arrays;

public class ByteArrayInOutStream extends ByteArrayOutputStream {
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	
    /**
     * Creates a new ByteArrayInOutStream. The buffer capacity is
     * initially 32 bytes, though its size increases if necessary.
     */
    public ByteArrayInOutStream() {
        super();
    }

    /**
     * Creates a new ByteArrayInOutStream, with a buffer capacity of
     * the specified size, in bytes.
     *
     * @param   size   the initial size.
     * @exception  IllegalArgumentException if size is negative.
     */
    public ByteArrayInOutStream(int size) {
        super(size);
    }
    
    public void write(final byte[] data) throws IOException {
    	try {
    		super.write(data);
    	}catch (Exception e) {}
    }
    
    public synchronized void write(byte b[], int off, int len) {
        if ((off < 0) || (off > b.length) || (len < 0) ||
            ((off + len) - b.length > 0)) {
            throw new IndexOutOfBoundsException();
        }
        if (!this.wrt) {
        	this.wrt = true;
        }
    	if (this.dy != null) {
    		this.dy.addData(b);
    	}
        if (b != null && buf != null) {
            ensureCapacity(count + len);
            System.arraycopy(b, off, buf, count, len);
            count += len;
        }
    }
    
    private void ensureCapacity(int minCapacity) {
        // overflow-conscious code
    	if (this.buf != null) {
            if (minCapacity - buf.length > 0)
                grow(minCapacity);
    	}
    }
    
    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = buf.length;
        int newCapacity = oldCapacity << 1;
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        buf = Arrays.copyOf(buf, newCapacity);
    }
    
    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

    /**
     * Creates a new ByteArrayInputStream that uses the internal byte array buffer 
     * of this ByteArrayInOutStream instance as its buffer array. The initial value 
     * of pos is set to zero and the initial value of count is the number of bytes 
     * that can be read from the byte array. The buffer array is not copied. This 
     * instance of ByteArrayInOutStream can not be used anymore after calling this
     * method.
     * @return the ByteArrayInputStream instance
     */
    public ByteArrayInputStream getInputStream() {
        // create new ByteArrayInputStream that respects the current count
        ByteArrayInputStream in = new ByteArrayInputStream(this.buf, 0, this.count);

        // set the buffer of the ByteArrayOutputStream 
        // to null so it can't be altered anymore
        this.buf = null;

        return in;
    }
    
    private DynamicByteArrayInputStream dy;
    public DynamicByteArrayInputStream getDInputStream() {
    	if (this.dy == null) {
    		this.dy = new DynamicByteArrayInputStream(this.buf);
    	}
    	return this.dy;
    }

    private boolean wrt;
	public boolean writtenTo() {
		return this.wrt;
	}

	public boolean hasData() {
		if (this.dy != null) {
			return this.dy.hasData();
		}
		return this.buf.length > 0;
	}

	
	public boolean isClosed() {
		return this.closed;
	}
	
	private boolean closed;
	
	@Override
	public void close() throws IOException {
		super.close();
		this.closed = true;
	}
}
